/*
 * File:   btree.h
 * Author: Stripped
 *
 * Created on 16 de abril de 2009, 16:19
 */

#ifndef _BTREEINDEX_H
#define _BTREEINDEX_H

#include "Node.h"
#include "LeafNode.h"
#include "File.h"
#include <string>
#include <cstdio>
#include <sstream>

using namespace std;

template <class TKey, class TValue>
class KeyValuePair;
/*
 * Preconditions:
 * Key must be comparable (have overloaded >, <, =, >=, <= operators)
 * Key and Value must have <<, >> overriden.
 */
template <class TKey, class TValue>
class BTreeIndex {
public:

    //typedef KeyValuePair<TKey, TValue> KeyValuePair;

    /**
     * Constructor: Creates the BTreeIndex
     *
     * @param  nodeMaxSize: is the max size of the node, default value = 2
     */
    BTreeIndex(unsigned int nodeMaxSize = 2, const string fileName = "")
    {
        this->root = new LeafNode<TKey, TValue>(nodeMaxSize);
        this->file = NULL;

        if (fileName.size() != 0)
        {
            this->file = new File(fileName);
        }

        this->size = 0;
    }

    /**
     * Destructor
     *
     */
    virtual ~BTreeIndex()
    {
        delete this->root;
        if (this->file != NULL)
        {
            this->file->close();
            delete this->file;
        }
    }

    /**
     * Adds a key-value pair into the tree.
     *
     * Throws an logic_error if the key exists in the tree.
     *
     * @param  TKey key which identifies the key-value pair
     * @param  TValue value of the key-value pair
     */
    bool put(const TKey& key, const TValue& value)
    {
        // Add the key-value pair into the root node, it will know where to insert it
        Node<TKey, TValue>* newParent = this->root->put(key, value);

        // a new node was returned, this means that the tree has grown in height and there is a new parent node.
        if (newParent != NULL)
        {
            this->root = newParent;
        }

        this->size++;

        return true;
    }

    /**
     * Returns the value of a specified key
     *
     * Throws logic_error if the key does not exists
     *
     * @param  TKey key which value you wish to retrieve
     * @return TValue value which matches the given key
     */
    TValue& get(const TKey& key)const
    {
        // ask the key to the root node, it will know how to find it
        return root->get(key);
    }

    /**
     * Searchs for a specified key and returns true if exists
     *
     * @param  TKey key which value you wish to find
     * @return bool True if the key exists, False if it doesn't
     */
    bool has(const TKey& key)const
    {
        // ask the key to the root node, it will know how to find it
        return root->has(key);
    }

    /**
     * counts the amount of elements in the tree.
     *
     * @return returns the elements count
     */
    unsigned int nodesCount()const
    {
        // TODO: should rename this method.

        // count the keys of the root node, it will know how to find it
        // return root->elementsCount();
        return this->size;
    }

    /**
     * Removes an element from the tree.
     * NOTE: Since remove is only intended as part of an update, it does not fixes underflow after deletion.
     *
     * Throws logic_error if the key is not present
     *
     * @param key to remove
     */
    void remove(const TKey& key)
    {
        // remove the key from the root node, it will know how to find it and remove it
        root->remove(key);
        this->size--;
    }

    /**
     * For debuggin purposes, it prints the tree
     *
     */
    void printTree()
    {
        root->printNode();
    }

    /**
     * loads the tree from the defined path
     *
     * Does not destroy the tree, it only adds the new elements.
     */
    void load()
    {
        this->file->openOrCreate();
        this->file->seekStart();

        unsigned int length = this->file->readLength();
        unsigned int pos = 0;

        while ((!this->file->eof()) && (pos < length))
        {
            KeyValuePair<TKey, TValue> element;
            this->file->readNext(&element);
            this->put(element.key, element.value);

            pos++;
        }

        this->file->close();
    }

    /**
     * saves the tree in the defined path
     *
     */
    void save()
    {
        std::remove(this->file->getPath().c_str());

        this->file->openOrCreate();

        vector< KeyValuePair<TKey, TValue> > elements = this->getAllElements();

        this->file->setLength((unsigned int) elements.size());

        for (unsigned int i = 0; i < elements.size(); i++)
        {
            this->file->save(elements[i]);
        }

        this->file->close();
    }

private:
    Node<TKey, TValue>* root;
    BTreeIndex<TKey, TValue>(const BTreeIndex<TKey, TValue>&) {}
    BTreeIndex<TKey, TValue>& operator=(const BTreeIndex<TKey, TValue>&) {}

    File* file;
    unsigned int size;

    vector< KeyValuePair<TKey, TValue> > getAllElements()
    {
        vector< KeyValuePair<TKey, TValue> > result;
        result.reserve(this->size);

        root->getAllElements(result);

        return result;
    }
};

#endif //_BTREEINDEX_H
